<?php

namespace App\Jobs;

use App\User;
use App\UserCoinLog;
use App\GameZghpt;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Intervention\Image\Facades\Image;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;

/**
 * 蚂蚁特工队-入队任务
 */
class JoinTeam implements ShouldQueue {
    use InteractsWithQueue, Queueable, SerializesModels;

    //蚂蚁特工队
    const bussiness_id  = 8;

    //用户标识
    protected $uid;

    //奖励层级
    protected $reward_count = 2;

    const lua = <<<LUA
local uid_list = cjson.decode(ARGV[1]);
for key, value in ipairs(uid_list) do
    if (math.mod(key, 2) == 0) then
        redis.call('rpush', 'ant:team:' .. ARGV[2] , value);
    else
        redis.call('rpush', 'ant:team:' .. ARGV[3], value);
    end
end
return uid_list;
LUA;

    // 配置
    protected $config = [
        // 餐厅各楼层奖励金钱数量
        'lv_xyb' => [
            1 => [ 1 => 200,  2 => 400,  3 => 200,  4 => 100,  5 => 10 ],
            2 => [ 1 => 400,  2 => 1200, 3 => 600,  4 => 300,  5 => 10 ],
            3 => [ 1 => 8000, 2 => 4000, 3 => 2000, 4 => 1000, 5 => 10 ],
            4 => [ 1 => 8000, 2 => 4000, 3 => 2000, 4 => 1000, 5 => 10 ],
            5 => [ 1 => 8000, 2 => 4000, 3 => 2000, 4 => 1000, 5 => 10 ],
            6 => [ 1 => 8000, 2 => 4000, 3 => 2000, 4 => 1000, 5 => 10 ],
            7 => [ 1 => 8000, 2 => 4000, 3 => 2000, 4 => 1000, 5 => 10 ],
            8 => [ 1 => 8000, 2 => 4000, 3 => 2000, 4 => 1000, 5 => 10 ],
        ]
    ];

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct($uid) {
        $this->uid = $uid;
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle() {
        Log::debug("debug Join Team: " . $this->uid . ", time: " + microtime(true));

        $redis = $this->getRedis();

        // 异常则堵塞队列
        while($redis->exists('ant:alart')) {
            sleep(5);
            Log::error("队列服务不可用");
        }

        // 入队用户ID
        $uid = $this->uid;
        // while (!$redis->set("ant:join:team", 'Y', Array('nx', 'px' => 1000))) {
        //     usleep(100);
        // }

        // 领队ID
        $leader_uid = 0;

        // 获取用户信息，仅允许不在队伍的用户入队
        DB::setFetchMode(\PDO::FETCH_ASSOC);
        $ant_user = DB::table('game_ant_user')->where('uid', $uid)->where('team_id', '')->first();
        if (empty($ant_user)) {
            $msg = sprintf('用户%s不存在或已入队', $uid);
            return $this->alarmSms($msg);
        }

        // 确定用户加入队伍
        $team = null;

        // 检查入队用户等级
        $user_level = in_array($ant_user['level'], [1, 2, 3, 4, 5, 6, 7, 8]) ? $ant_user['level'] : 1;

        // 查找上级用户所在队伍
        if (!empty($ant_user['finder_id'])) {
            $finder_id = $ant_user['finder_id'];
            $team = DB::table('game_ant_team')
                ->where('level', $user_level)
                ->where('status', 1)
                ->where('uid_list', 'like', "%\"$finder_id\"%")
                ->first();
        }

        // 查找与用户同级别队伍（优先人数最少）
        if (empty($team)) {
            $team = DB::table('game_ant_team')
                ->where('level', $user_level)
                ->where('status', 1)
                ->orderBy('count', 'asc')
                ->first();
        }

        // 如果队伍不存在，标记入队服务不可用，短信告警
        if (empty($team)) {
            $redis->set('ant:alarm', time(), ['nx']);
            $msg = sprintf("用户%s入队失败，无法获取同级别%s队伍", $uid, $user_level);
            return $this->alarmSms($msg);
        }

        // 选中队伍，锁定队伍，防止同一队伍被多用户并发选中
        $team_id = $team['tid'];
        $redis->set("ant:lock:team:$team_id", time(), ['nx']);

        // 普通入队：用户未入队且选中的队伍人数小于30，
        if ($ant_user['team_id'] === '' && intval($team['count']) < 30) {
            DB::transaction(function () use($team_id, $uid, $redis, $team) {
                $uid_list = array_push(json_decode($team['uid_list'], true), $uid);
                DB::table('game_ant_user')->where('uid', $uid)->where('team_id', '')->update([ 'team_id' => $team_id ]);
                DB::table('game_ant_team')->where('tid', $team_id)->increment('count', 1, ['uid_list' => json_encode($uid_list)]);
                $redis->rPush("ant:team:$team_id", "$uid");
            });
        }

        // 临界入队：用户未入队且选中的队伍人数等于30
        if (intval($team['count']) === 30 && intval($team['status']) === 1) {

            DB::transaction(function () use($redis, $team_id, $uid, $user_level, $team, &$leader_uid) {

                // 更新用户入队状态
                $redis->rPush("ant:team:$team_id", "$uid");
                DB::table('game_ant_user')->where('uid', $uid)->where('team_id', '')->update(['team_id' => $team_id]);

                // 锁定队伍，记录队伍临界状态
                $uid_list = $redis->lRange("ant:team:$team_id", 0, -1);
                $uid_1    = $redis->lRange("ant:team:$team_id", 0, 0);
                $uid_2    = $redis->lRange("ant:team:$team_id", 1, 2);
                $uid_4    = $redis->lRange("ant:team:$team_id", 3, 6);
                $uid_8    = $redis->lRange("ant:team:$team_id", 7, 14);
                $uid_16   = $redis->lRange("ant:team:$team_id", 15, -1);
                DB::table('game_ant_team')->where('tid', $team_id)->increment('count', 1, [
                    'status'   => 2, // 锁定队伍
                    'uid_list' => json_encode($uid_list),
                    'uid_1'    => json_encode($uid_1),
                    'uid_2'    => json_encode($uid_2),
                    'uid_4'    => json_encode($uid_4),
                    'uid_8'    => json_encode($uid_8),
                    'uid_16'   => json_encode($uid_16),
                ]);

                // 发送奖励
                $this->batchUpdateUserXYB($uid_1,   $this->config['lv_xyb'][$user_level][1]);
                $this->batchUpdateUserXYB($uid_2,   $this->config['lv_xyb'][$user_level][2]);
                $this->batchUpdateUserXYB($uid_4,   $this->config['lv_xyb'][$user_level][3]);
                $this->batchUpdateUserXYB($uid_8,   $this->config['lv_xyb'][$user_level][4]);
                $this->batchUpdateUserXYB($uid_16,  $this->config['lv_xyb'][$user_level][5]);

                // 剔除队长
                $leader_uid = array_shift($uid_list);
                $left_team_id  = uniqid('', false);
                $right_team_id = uniqid('', false);

                // 首先创建内存队列，持久化后可见
                $params = [ json_encode($uid_list), $left_team_id, $right_team_id ];
                $data = $redis->eval(self::lua, $params);

                // 升级队长级别，清除队长所在队伍
                if ($user_level >= 8) {
                    $leader_uid = 0;
                } else {
                    DB::table('game_ant_user')->where('uid', $leader_uid)->update(['team_id' => '', 'level' => intval($user_level) + 1]);
                }
                $left_team = [];
                $right_team = [];
                foreach($uid_list as $idx => $uid) {
                    if ($idx % 2 == 0) {
                        array_push($left_team, $uid);
                    } else {
                        array_push($right_team, $uid);
                    }
                }

                // 新建2支队伍
                DB::table('game_ant_team')->insert([
                    [ 'tid' => $left_team_id, 'level' => $user_level, 'count' => count($left_team), 'uid_list' => json_encode($left_team) ],
                    [ 'tid' => $right_team_id, 'level' => $user_level, 'count' => count($right_team), 'uid_list' => json_encode($right_team) ],
                ]);

                // 更成员新所属Team
                DB::table('game_ant_user')->whereIn('uid', $left_team)->update([ 'team_id' => $left_team_id ]);
                DB::table('game_ant_user')->whereIn('uid', $right_team)->update([ 'team_id' => $right_team_id ]);
            });

        }

        if ($leader_uid > 0) {
            Log::debug(sprintf('发送队长%s至其他队伍', $leader_uid));
            dispatch(new JoinTeam($leader_uid));
        }

        // 入队完毕，解除锁定队伍
        $redis->delete("ant:lock:team:$team_id");

        Log::debug(sprintf("用户%s入队成功", $uid));
    }

    /**
     * 获取用户个人信息
     *
     * @return [type] [description]
     */
    public function getUserInfo() {
        $id = $this->getLoginUser();
        DB::setFetchMode(\PDO::FETCH_ASSOC);
        $user = DB::table('user')->where('id', $id)->first();
        $user['XYB'] = UserCoinLog::totalXyb($user['id']) * 100;
        $this->echoJson(0, '', $user);
    }

    /**
     * 批量更新多用户小妖币
     *
     * @param  [type] $uid  [description]
     * @param  [type] $gold [description]
     * @return [type]       [description]
     */
    private function batchUpdateUserXYB($uid_list , $gold) {
        foreach($uid_list as $uid) {
            $key = sprintf("%s-%s-%s-%s", self::bussiness_id, $uid, $gold, time());
            $tips = $gold > 0 ? '蚂蚁特工队赢得小妖币' : '蚂蚁特工队消耗小妖币';
            UserCoinLog::log(self::bussiness_id, $uid, UserCoinLog::TYPE_XYB, $gold, $tips, $key);
        }
    }

    /**
     * 更新用户小妖币
     *
     * @param  [type] $uid  [description]
     * @param  [type] $gold [description]
     * @return [type]       [description]
     */
    private function updateUserXYB($uid, $gold) {
        $key = sprintf("%s-%s-%s-%s", self::bussiness_id, $uid, $gold, time());
        $tips = $gold > 0 ? '蚂蚁特工队赢得小妖币' : '蚂蚁特工队消耗小妖币';
        UserCoinLog::log(self::bussiness_id, $uid, UserCoinLog::TYPE_XYB, $gold, $tips, $key);
    }

    /**
     * 统计当日盈亏（记录保留7天）
     *
     * @param  [type] $gold [description]
     * @return [type]       [description]
     */
    private function statDayProfitAndLoss($gold) {
        $redis = $this->getRedis();
        $day_stat_key = sprintf("hlgc:stat:%s", date('Ymd', time()));
        $redis->incrBy($day_stat_key, $gold);
        $redis->setTimeout($day_stat_key, 7*24*60*60);
    }

    /**
     * 告警（短信）
     *
     * @param  string $phone   [电话]
     * @param  string $message [信息]
     * @return [type]          [description]
     */
    private function alarmSms($message = '告警邮件', $phone = '15011931913') {
        Log::error($message);
        try {
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_URL, "http://sms-api.luosimao.com/v1/send.json");
            curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
            curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 8);
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
            curl_setopt($ch, CURLOPT_HEADER, FALSE);
            curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
            curl_setopt($ch, CURLOPT_USERPWD, 'api:key-5145112f10b704bbd5592f80402c77b5');
            curl_setopt($ch, CURLOPT_POST, TRUE);
            curl_setopt($ch, CURLOPT_POSTFIELDS, array('mobile' => $phone, 'message' => $message));
            $res = curl_exec($ch);
            curl_close( $ch );
        } catch (\Exception $e) {
            Log::error("发送告警邮件失败！" . $e->getMessage());
        }
    }

    /**
     * 验证登录
     *
     * @return [type] [description]
     */
    private function getLoginUser() {
        // 检查用户登录态
        $user = auth()->user();
        $uid = $user->id;
        return $uid;
    }

    /**
     * 获取Redis实例
     *
     * @return [type] [description]
     */
    private function getRedis() {
        static $redis = null;
        if (empty($redis)) {
            $redis = new \Redis();
            $redis->connect(env('REDIS_HOST'), env('REDIS_PORT'));
            $redis->auth(env('REDIS_PASSWORD'));
        }
        return $redis;
    }

    /**
     * 响应JSON/JSONP格式数据
     *
     * @param  integer $code [返回代码：0 成功 1 失败]
     * @param  string  $msg  [错误信息]
     * @param  array   $data [返回数据]
     * @return [type]        [description]
     */
    private function echoJson($code = 0, $msg = '', $data = []) {
        header('Content-type: application/json');
        $result = [
            'code'  => $code,
            'msg'   => $msg,
            'data'  => $data,
        ];
        $json = json_encode($result);

        // JSONP
        if (!empty($_GET['callback'])) {
            $json = $_GET['callback'] . "(" . $json . ")";
        }
        echo $json;
        exit(0);
    }

    /**
     * 奖励上级
     */
    public function rewardFinder($user, $total) {
        if ($this->reward_count) {
            if ($finder = User::where('id', $user['finder_id'])->first()) {
                if (floatval($xyb = bcmul($total, 0.01, 2))) {
                    if(!$gameZghpt = $finder->game_zghpt){
                        $gameZghpt = new GameZghpt();
                        $gameZghpt->user_id = $finder->id;
                        $gameZghpt->quota = 0;
                        $gameZghpt->level = 0;
                        $gameZghpt->save();
                    }
                    if($gameZghpt->level) {
                        $tmp_xyb = $xyb * 100;
                        $message = '恭喜您获得' . $tmp_xyb . '个小妖币的推荐奖励。';

                        $key = '8-'. $finder->id . '-' . $xyb . '-' . time() . '-1';
                        UserCoinLog::log( 8, $finder->id, UserCoinLog::TYPE_XYB, $xyb, '推荐奖励', $key );
                    } else {
                        $message = '您好，您错过了'. $xyb .'小妖币的红包，请<a href="http://ga.cloudxt.cn/zghpt/vip/page">立即升级VIP</a>！';
                    }

                    $this->dispatch(new SendWechatMessage($finder->openid, $message));

                    $this->reward_count--;
                    $this->rewardFinder($finder, $total);
                }
            }
        }
    }
}
